package jp.co.cos.ittrick.channel.service;

import java.util.Date;
import java.util.List;

import jp.co.cos.ittrick.channel.Constants;
import jp.co.cos.ittrick.channel.common.exception.DatastorePutException;
import jp.co.cos.ittrick.channel.common.exception.DoubleInsertException;
import jp.co.cos.ittrick.channel.meta.ChannelModelMeta;
import jp.co.cos.ittrick.channel.model.ChannelModel;

import org.slim3.datastore.Datastore;
import org.slim3.memcache.Memcache;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.Transaction;


public class ChannelService {

    /**
     * すべてのチャンネルリストを取得
     * @return チャンネルリスト
     */
    public List<ChannelModel> getAll() {

        // Memcache に存在した場合はMemcache内のmodelを返す
        List<ChannelModel> channelList = Memcache.get(Constants.CHANNEL_LIST_MEMCACHE_KEY);
        if(channelList != null && channelList.size() > 0) return channelList;

        // DBから取得し、存在した場合はMemcacheに入れる
        ChannelModelMeta meta = new ChannelModelMeta();
        channelList = Datastore.query(meta).sort(meta.index.asc).asList();
        if(channelList != null && channelList.size() > 0) Memcache.put(Constants.CHANNEL_LIST_MEMCACHE_KEY, channelList);

        return channelList;
    }

    /**
     * キーによるチャンネルの取得
     * @param pkKey
     * @return ChannelModel
     */
    public ChannelModel getChannelByKey(String pkKey) {
        Key key = Datastore.createKey(ChannelModelMeta.get(), pkKey);

        // Memcache に存在した場合はMemcache内のmodelを返す
        ChannelModel channelModel = Memcache.get(key);
        if(channelModel != null) return channelModel;

        // DBから取得し、存在した場合はMemcacheに入れる
        channelModel = Datastore.getOrNull(ChannelModelMeta.get(), key);
        if(channelModel != null) Memcache.put(key, channelModel);

        return channelModel;
    }

    /**
     * Insert
     * @param pkKey
     * @param model
     * @return key
     * @throws DoubleInsertException
     * @throws DatastorePutException
     */
    public Key insert(String pkKey, ChannelModel model)
    throws DoubleInsertException, DatastorePutException {

        // ２重登録チェック
        if(getChannelByKey(pkKey) != null) {
            throw new DoubleInsertException();
        }

        // キーの作成
        Key key = Datastore.createKey(ChannelModelMeta.get(), pkKey);
        model.setKey(key);
        model.setLastmod(new Date());

        // insert
        Transaction tx = Datastore.beginTransaction();
        try {
            key = Datastore.put(tx, model);
            tx.commit();

            // チャンネルリストのMemcacheを削除する
            Memcache.delete(Constants.CHANNEL_LIST_MEMCACHE_KEY);
        }
        catch (Exception e) {
            if (tx.isActive()) {
                tx.rollback();
            }
            throw new DatastorePutException();
        }

        return key;
    }


    /**
     * Update
     * @param model
     * @throws DatastorePutException
     */
    public void update(ChannelModel model) throws DatastorePutException {

        model.setLastmod(new Date());

        // Local Transaction の開始
        Transaction tx = Datastore.beginTransaction();

        try {
            // バージョンによる変更チェック
            Datastore.get(tx, ChannelModelMeta.get(), model.getKey(), model.getVersion());
            // 更新
            Datastore.put(tx, model);

            tx.commit();

            // Memcacheを削除する
            Memcache.delete(model.getKey());
            // チャンネルリストのMemcacheを削除する
            Memcache.delete(Constants.CHANNEL_LIST_MEMCACHE_KEY);

        }catch (Exception e) {
            if (tx.isActive()) {
                tx.rollback();
            }
            throw new DatastorePutException();
        }
    }

    /**
     * 削除
     * @param serialPkKey
     * @param articleNum
     * @param articlePageNum
     * @throws Exception
     */
    public void delete(String pkKey) throws Exception {

        ChannelModel channelModel = getChannelByKey(pkKey);
        Key key = channelModel.getKey();

        // Local Transaction の開始
        Transaction tx = Datastore.beginTransaction();

        try {
            Datastore.delete(tx, key);

            tx.commit();

        }catch (Exception e) {
            if (tx.isActive()) {
                tx.rollback();
            }
            throw e;
        }
    }

}
